package com.strive.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.strive.entity.SysMenu;
import com.strive.entity.SysRole;
import com.strive.entity.SysUser;
import com.strive.mapper.SysUserMapper;
import com.strive.service.SysMenuService;
import com.strive.service.SysRoleService;
import com.strive.service.SysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.strive.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author strive.Peng
 * @since 2024-03-14
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    //*注入需要的操作方法
    @Autowired
    SysRoleService sysRoleService;

    @Autowired
    SysUserMapper sysUserMapper;

    @Autowired
    SysMenuService sysMenuService;

    // 权限的缓存 引入redis
    @Autowired
    RedisUtil redisUtil;

    @Override
    public SysUser getByUsername(String username) {
        // 使用mybatis的写法
        return getOne(new QueryWrapper<SysUser>().eq("username", username));
    }

    /**
     * 获取用户权限信息
     *
     * @param userId
     * @return 用户权限的字符串
     */
    @Override
    public String getUserAuthorityInfo(Long userId) {
        // 通过ID获取一个用户
        SysUser sysUser = sysUserMapper.selectById(userId);
        String authority = "";

        // 先判断redis中是否缓存了权限信息
        if (redisUtil.hasKey("GrantedAuthority:" + sysUser.getUsername())) {
            // 强制转换
            authority = (String) redisUtil.get("GrantedAuthority:" + sysUser.getUsername());
        } else {
            // 如果没有就重新查库

            // 获取角色编码 使用sql语句查询 涉及到两个表的查询
            List<SysRole> roles = sysRoleService.list(new QueryWrapper<SysRole>().
                    // sql语句  select * from sys_role where id in (select role_id from sys_user_role where user_id = $user_id )
                            inSql("id", "select role_id from sys_user_role where user_id = " + userId));
            if (roles.size() > 0) {
                // 转化为流的方式 以，隔开, 同时添加上前缀 返回的结果格式如下： ROLE_admin
                String roleCodes = roles.stream().map(r -> "ROLE_" + r.getCode()).collect(Collectors.joining(","));
                authority = roleCodes;
            }

            // 获取菜单编码操作 涉及到三个表的查询
            List<Long> menuIds = sysUserMapper.getNavMenuIds(userId);
            if (menuIds.size() > 0) {
                // 根据menuID 继续查库 获取所有的menu
                List<SysMenu> menus = sysMenuService.listByIds(menuIds);
                // 以流的方式获取用户菜单权限数据
                String menuPerms = menus.stream().map(r -> r.getPerms()).collect(Collectors.joining(","));
                // 将菜单数据也添加到权限字符串中
                authority = authority.concat(",").concat(menuPerms);
            }

            /**
             * 获取到权限信息，存入redis
             * 缓存的关键字： GrantedAuthority+用户名， 缓存的数据是权限信息， 缓存的时间是1小时 （单位是秒）
             */
            redisUtil.set("GrantedAuthority:" + sysUser.getUsername(), authority, 60 * 60);
        }
        return authority;
    }

    @Override
    public void clearUserAuthorityInfo(String username) {
        redisUtil.del("GrantedAuthority:" + username);
    }

    /**
     * 角色信息发生变化后重置用户的权限
     *
     * @param roleId
     */
    @Override
    public void clearUserAuthorityInfoByRoleId(Long roleId) {
        // 获取role相关的userID
        List<SysUser> users = this.list(new QueryWrapper<SysUser>()
                // 先找到roleID相关的userID再通过userID得到相关的用户信息
                .inSql("id", "select user_id from sys_user_role where role_id = " + roleId));
        users.forEach(u -> {
            // 调用本类的方法实现清空
            this.clearUserAuthorityInfo(u.getUsername());
        });
    }


    /**
     * 菜单编码信息发生变化后重置用户的权限
     *
     * @param menuId
     */
    @Override
    public void clearUserAuthorityInfoByMenuId(Long menuId) {
        List<SysUser> users = sysUserMapper.listByMenuId(menuId);
        users.forEach(u -> {
            // 调用本类的方法实现清空
            this.clearUserAuthorityInfo(u.getUsername());
        });
    }
}
